﻿using System;
using System.IO;
using System.Text;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;
using Un4seen.Bass;
using Un4seen.Bass.AddOn.Wma;
using Un4seen.Bass.AddOn.Mix;
using Un4seen.Bass.AddOn.Tags;
using Un4seen.Bass.Misc;
using Un4seen.Bass.AddOn.Fx;

namespace EditorDeAudioTT201095
{
    public partial class Microfono : Form
    {
        public Microfono()
        {
            InitializeComponent();
        }

        private int _wmaPlugIn = 0;
        private int _encHandle = 0;
        private string nombreArchivo = String.Empty;
        private string _fileNameOutput = String.Empty;
        private byte[] _encBuffer = new byte[65536]; // buffer de codificación (32KB x 16-bit)
        private int[] equalizador = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        private bool espectroCompleto = true;
        private Visuals modificadorVisual = new Visuals();

        private void Encoder_Load(object sender, System.EventArgs e)
        {
            if (Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, this.Handle))
            {
                BASS_INFO info = new BASS_INFO();
                Bass.BASS_GetInfo(info);
                Console.WriteLine(info.ToString());

                _wmaPlugIn = Bass.BASS_PluginLoad("basswma.dll");

                int[] rates = BassWma.BASS_WMA_EncodeGetRates(44100, 2, BASSWMAEncode.BASS_WMA_ENCODE_RATES_CBR);

                BassFx.BASS_FX_GetVersion();
            }


            Bass.BASS_RecordInit(-1);
        }

        private void Encoder_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            buttonDetenerGrabacion.PerformClick();
            Bass.BASS_StreamFree(monStream);
            Bass.BASS_Stop();
            Bass.BASS_Free();
            Bass.BASS_PluginFree(_wmaPlugIn);
        }

        private int _mixerStream = 0;
        private int _streamA = 0;
        private int _streamB = 0;
        private void buttonPlaySource_Click(object sender, System.EventArgs e)
        {
            Bass.BASS_StreamFree(_streamA);
            Bass.BASS_StreamFree(_streamB);
            Bass.BASS_StreamFree(_mixerStream);

            _streamA = Bass.BASS_StreamCreateFile(nombreArchivo, 0, 0, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT);
            _streamB = Bass.BASS_StreamCreateFile(_fileNameOutput, 0, 0, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT);
            BASS_CHANNELINFO i = new BASS_CHANNELINFO();
            Bass.BASS_ChannelGetInfo(_streamA, i);

            _mixerStream = BassMix.BASS_Mixer_StreamCreate(i.freq, 4, BASSFlag.BASS_DEFAULT);

            bool okA = BassMix.BASS_Mixer_StreamAddChannel(_mixerStream, _streamA, BASSFlag.BASS_MIXER_MATRIX | BASSFlag.BASS_STREAM_AUTOFREE);
            bool okB = BassMix.BASS_Mixer_StreamAddChannel(_mixerStream, _streamB, BASSFlag.BASS_STREAM_AUTOFREE);

            float[,] matrixA = new float[4, 2] { // estereo a matriz cuadrada
												{1, 0}, // salida izquierda frontal = entrada izquierda
												{0, 1}, // salida derecha frontal = entrada derecha
												{1, 0}, // salida izquierda trasera = entrada izquierda
												{0, 1}  // rsalida derecha trasera = entrada derecha
											   };
            BassMix.BASS_Mixer_ChannelSetMatrix(_streamA, matrixA);
            // just so show how to get it back...
            float[,] matrixGet = new float[4, 2];
            BassMix.BASS_Mixer_ChannelGetMatrix(_streamA, matrixGet);
            // mute streamB at the beginning

            Bass.BASS_ChannelSetAttribute(_streamB, BASSAttribute.BASS_ATTRIB_VOL, 0f);

            // and play it...
            if (Bass.BASS_ChannelPlay(_mixerStream, false))
                this.label1.Text = "Reproduciendo usando degradación cruzada";
        }

        private void buttonStop_Click(object sender, System.EventArgs e)
        {
            Bass.BASS_ChannelStop(_mixerStream);
            Bass.BASS_StreamFree(_streamA);
            Bass.BASS_StreamFree(_streamB);
            Bass.BASS_StreamFree(_mixerStream);
        }

        private WaveForm WF = null;
        private BASSBuffer monBuffer = new BASSBuffer(2f, 44100, 2, 16); // 44.1kHz, 16-bit, estereo
        private int monStream = 0;
        private STREAMPROC monProc = null;
        EncoderLAME lame = null;

        private void buttonStartRec_Click(object sender, System.EventArgs e)
        {
            this.buttonStartRec.Enabled = false;
            this.labelRec.Text = "Grabando...";

            _myRecProc = new RECORDPROC(MyRecoring);
            manejadorGrabacion = Bass.BASS_RecordStart(44100, 2, BASSFlag.BASS_RECORD_PAUSE, _myRecProc, new IntPtr(_encHandle));
            
            lame = new EncoderLAME(manejadorGrabacion);
            lame.InputFile = null;	//STDIN
            lame.OutputFile = "pruebaGrabacion.mp3";
            lame.LAME_Bitrate = (int)EncoderLAME.BITRATE.kbps_192;
            lame.LAME_Mode = EncoderLAME.LAMEMode.Default;
            lame.LAME_TargetSampleRate = (int)EncoderLAME.SAMPLERATE.Hz_44100;
            lame.LAME_Quality = EncoderLAME.LAMEQuality.Quality;

            monBuffer.Clear();
            checkBoxMonitor.Checked = false;

            // Crea la forma de onda relacionada a la grabación
            WF = new WaveForm();
            WF.FrameResolution = 0.01f; // 10ms 
            // start a live recording waveform with 5sec. init size and 2sec. next size
            WF.RenderStartRecording(manejadorGrabacion, 5f, 2f);

            // Inicia la grabación
            lame.Start(null, IntPtr.Zero, false);
            Bass.BASS_ChannelPlay(manejadorGrabacion, false);

            // Crea el flujo
            monStream = Bass.BASS_StreamCreate(44100, 2, 0, monProc, IntPtr.Zero);

            // EQ - Ecualizador de 10 bandas
            BASS_DX8_PARAMEQ eq = new BASS_DX8_PARAMEQ();
            equalizador[0] = Bass.BASS_ChannelSetFX(manejadorGrabacion, BASSFXType.BASS_FX_DX8_PARAMEQ, 0);
            equalizador[1] = Bass.BASS_ChannelSetFX(manejadorGrabacion, BASSFXType.BASS_FX_DX8_PARAMEQ, 0);
            equalizador[2] = Bass.BASS_ChannelSetFX(manejadorGrabacion, BASSFXType.BASS_FX_DX8_PARAMEQ, 0);
            equalizador[3] = Bass.BASS_ChannelSetFX(manejadorGrabacion, BASSFXType.BASS_FX_DX8_PARAMEQ, 0);
            equalizador[4] = Bass.BASS_ChannelSetFX(manejadorGrabacion, BASSFXType.BASS_FX_DX8_PARAMEQ, 0);
            equalizador[5] = Bass.BASS_ChannelSetFX(manejadorGrabacion, BASSFXType.BASS_FX_DX8_PARAMEQ, 0);
            equalizador[6] = Bass.BASS_ChannelSetFX(manejadorGrabacion, BASSFXType.BASS_FX_DX8_PARAMEQ, 0);
            equalizador[7] = Bass.BASS_ChannelSetFX(manejadorGrabacion, BASSFXType.BASS_FX_DX8_PARAMEQ, 0);
            equalizador[8] = Bass.BASS_ChannelSetFX(manejadorGrabacion, BASSFXType.BASS_FX_DX8_PARAMEQ, 0);
            equalizador[9] = Bass.BASS_ChannelSetFX(manejadorGrabacion, BASSFXType.BASS_FX_DX8_PARAMEQ, 0);

            eq.fBandwidth = 18f;

            eq.fCenter = 31f;
            eq.fGain = this.trackBarEQBanda31.Value / 10f;
            Bass.BASS_FXSetParameters(equalizador[0], eq);

            eq.fCenter = 63f;
            eq.fGain = this.trackBarEQBanda31.Value / 10f;
            Bass.BASS_FXSetParameters(equalizador[1], eq);

            eq.fCenter = 125f;
            eq.fGain = this.trackBarEQBanda31.Value / 10f;
            Bass.BASS_FXSetParameters(equalizador[2], eq);

            eq.fCenter = 250f;
            eq.fGain = this.trackBarEQBanda31.Value / 10f;
            Bass.BASS_FXSetParameters(equalizador[3], eq);

            eq.fCenter = 500f;
            eq.fGain = this.trackBarEQBanda63.Value / 10f;
            Bass.BASS_FXSetParameters(equalizador[4], eq);

            eq.fCenter = 1000f;
            eq.fGain = this.trackBarEQBanda31.Value / 10f;
            Bass.BASS_FXSetParameters(equalizador[5], eq);

            eq.fCenter = 2000f;
            eq.fGain = this.trackBarEQBanda31.Value / 10f;
            Bass.BASS_FXSetParameters(equalizador[6], eq);

            eq.fCenter = 4000f;
            eq.fGain = this.trackBarEQBanda31.Value / 10f;
            Bass.BASS_FXSetParameters(equalizador[7], eq);

            eq.fCenter = 8000f;
            eq.fGain = this.trackBarEQBanda125.Value / 10f;
            Bass.BASS_FXSetParameters(equalizador[8], eq);

            eq.fCenter = 16000f;
            eq.fGain = this.trackBarEQBanda125.Value / 10f;
            Bass.BASS_FXSetParameters(equalizador[9], eq);
        }

        private void buttonStopRec_Click(object sender, System.EventArgs e)
        {
            Bass.BASS_ChannelStop(manejadorGrabacion);
            lame.Stop();
            this.buttonStartRec.Enabled = true;
            this.labelRec.Text = "Grabación detenida";
            if (WF != null)
            {
                WF.RenderStopRecording();

                this.pictureBoxFormaDeOnda.BackgroundImage = WF.CreateBitmap(this.pictureBoxFormaDeOnda.Width, this.pictureBoxFormaDeOnda.Height, -1, -1, true);
            }
            if (checkBoxMonitor.Checked)
            {
                checkBoxMonitor.Checked = false;
            }
        }

        private void checkBoxMonitor_CheckedChanged(object sender, System.EventArgs e)
        {
            if (checkBoxMonitor.Checked)
            {
                monProc = new STREAMPROC(MonitoringStream);
                monStream = Bass.BASS_StreamCreate(44100, 2, 0, monProc, IntPtr.Zero);
                Bass.BASS_ChannelPlay(monStream, false);
            }
            else
            {
                Bass.BASS_StreamFree(monStream);
            }
        }

        private int efectoFlange = 0;
        private void checkBoxFlange_CheckedChanged(object sender, EventArgs e)
        {
            if (monStream == 0)
                return;

            if (this.checkBoxFlange.Checked)
            {
                efectoFlange = Bass.BASS_ChannelSetFX(monStream, BASSFXType.BASS_FX_BFX_FLANGER, -4);
                BASS_BFX_FLANGER flanger = new BASS_BFX_FLANGER();
                flanger.Preset_Default();
                Bass.BASS_FXSetParameters(efectoFlange, flanger);

                BASS_BFX_FLANGER f = new BASS_BFX_FLANGER();
                Bass.BASS_FXGetParameters(efectoFlange, f);
            }
            else
            {
                Bass.BASS_ChannelRemoveFX(monStream, efectoFlange);
                efectoFlange = 0;
            }
        }

        private void buttonAplicarFlanger_Click(object sender, EventArgs e)
        {
            if (monStream == 0)
                return;

            BASS_BFX_FLANGER flanger = new BASS_BFX_FLANGER();

            Bass.BASS_FXGetParameters(efectoFlange, flanger);
            flanger.fSpeed = int.Parse(this.textBoxFlangerSpeed.Text);

            Bass.BASS_FXSetParameters(efectoFlange, flanger);
        }

        private int efectoEcho = 0;
        private void checkBoxEcho_CheckedChanged(object sender, EventArgs e)
        {
            if (monStream == 0)
                return;

            if (this.checkBoxEcho.Checked)
            {

                efectoEcho = Bass.BASS_ChannelSetFX(monStream, BASSFXType.BASS_FX_BFX_ECHO3, -4);
                BASS_BFX_ECHO3 echo = new BASS_BFX_ECHO3();
                echo.Preset_LongEcho();
                Bass.BASS_FXSetParameters(efectoEcho, echo);
            }
            else
            {
                Bass.BASS_ChannelRemoveFX(monStream, efectoEcho);
                efectoEcho = 0;
            }
        }

        private int MonitoringStream(int handle, IntPtr buffer, int length, IntPtr user)
        {
            return monBuffer.Read(buffer, length, user.ToInt32());
        }

        private RECORDPROC _myRecProc; // variable global para que el recolector de basura no lo puede remover
        private int manejadorGrabacion = 0;
        // La retrollamada de la grabación
        private unsafe bool MyRecoring(int handle, IntPtr buffer, int length, IntPtr user)
        {
            // 'user' contendrá el manejador de codificación
            if (length > 0 && buffer != IntPtr.Zero)
            {
                if (checkBoxMonitor.Checked)
                {
                    monBuffer.Write(buffer, length);
                }

                if (!this.buttonStartRec.Enabled)
                {
                    // if recording started...write the data to the encoder
                    BassWma.BASS_WMA_EncodeWrite(user.ToInt32(), buffer, length);
                }

                WF.RenderRecording(buffer, length);

                // Obtener los niveles de la grabación
                int maxL = 0;
                int maxR = 0;
                short* data = (short*)buffer;
                for (int a = 0; a < length / 2; a++)
                {
                    // decidir entre el canal derecho/izquierdo
                    if (a % 2 == 0)
                    {
                        // canal izquierdo
                        if (data[a] > maxL)
                            maxL = data[a];
                    }
                    else
                    {
                        // canal derecho
                        if (data[a] > maxR)
                            maxR = data[a];
                    }
                }
      
                if (maxL > 32768)
                    maxL = 32768;
                if (maxR > 32768)
                    maxR = 32768;

                this.BeginInvoke(new UpdateDelegate(UpdateDisplay), new object[] { maxL, maxR });
            }
            return true; // siempre continúa con la grabación
        }

        int _zoomDistance;
        int _zoomStart;
        int _zoomEnd;
        private void DrawWave()
        {
            if (WF != null)
            {
                _zoomDistance = 200; // = 5sec. resolusión es 0.01sec.
                _zoomEnd = WF.FramesRendered > _zoomDistance ? WF.FramesRendered : _zoomDistance;
                _zoomStart = _zoomEnd - _zoomDistance;

                this.pictureBoxFormaDeOnda.BackgroundImage = WF.CreateBitmap(this.pictureBoxFormaDeOnda.Width, this.pictureBoxFormaDeOnda.Height, _zoomStart, _zoomEnd, true);
                if (espectroCompleto)
                    this.pictureBoxEspectro.Image = modificadorVisual.CreateSpectrumLinePeak(manejadorGrabacion, this.pictureBoxEspectro.Width, this.pictureBoxEspectro.Height, Color.LightBlue, Color.Blue, Color.Aqua, Color.Black, 2, 1, 1, 13, false, true, false);
                else
                    this.pictureBoxEspectro.Image = modificadorVisual.CreateWaveForm(manejadorGrabacion, this.pictureBoxEspectro.Width, this.pictureBoxEspectro.Height, Color.Aqua, Color.Red, Color.Gray, Color.Linen, 1, true, false, true);
            }
            else
                this.pictureBoxFormaDeOnda.BackgroundImage = null;
        }

        public delegate void UpdateDelegate(int maxL, int maxR);
        private void UpdateDisplay(int maxL, int maxR)
        {
            this.progressBarRecL.Value = maxL;
            this.progressBarRecR.Value = maxR;
            DrawWave();
        }

        private void buttonAplicarEcho_Click(object sender, EventArgs e)
        {
            if (monStream == 0)
                return;

            BASS_BFX_ECHO3 echo = new BASS_BFX_ECHO3();

            Bass.BASS_FXGetParameters(efectoEcho, echo);
            echo.fDelay = float.Parse(this.textBoxEchoDelay.Text);

            Bass.BASS_FXSetParameters(efectoEcho, echo);
        }

        private int efectoReverb = 0;
        private void checkBoxReverb_CheckedChanged(object sender, EventArgs e)
        {
            if (monStream == 0)
                return;

            if (this.checkBoxReverb.Checked)
            {
                efectoReverb = Bass.BASS_ChannelSetFX(monStream, BASSFXType.BASS_FX_BFX_REVERB, -3);
                BASS_BFX_REVERB reverb = new BASS_BFX_REVERB();
                reverb.fLevel = 0.5f;
                Bass.BASS_FXSetParameters(efectoReverb, reverb);
            }
            else
            {
                // Remueve el DSP
                Bass.BASS_ChannelRemoveFX(monStream, efectoReverb);
                efectoReverb = 0;
            }
        }

        private void buttonAplicarReverb_Click(object sender, EventArgs e)
        {
            if (monStream == 0)
                return;

            BASS_BFX_REVERB reverb = new BASS_BFX_REVERB();

            Bass.BASS_FXGetParameters(efectoReverb, reverb);
            reverb.lDelay = int.Parse(this.textBoxRetrasoReverb.Text);

            Bass.BASS_FXSetParameters(efectoReverb, reverb);
        }

        private int _hpf = 0;
        private void checkBoxFiltro_CheckedChanged(object sender, EventArgs e)
        {
            if (monStream == 0)
                return;

            if (this.checkBoxFiltro.Checked)
            {
                _hpf = Bass.BASS_ChannelSetFX(monStream, BASSFXType.BASS_FX_BFX_BQF, 0);
                BASS_BFX_BQF hpfSettings = new BASS_BFX_BQF(BASSBFXBQF.BASS_BFX_BQF_HIGHPASS, (float)Convert.ToDouble(textBoxFrecAlta.Text),
                    0f, 0f, 0.7f, 0f, BASSFXChan.BASS_BFX_CHANALL);
                Bass.BASS_FXSetParameters(_hpf, hpfSettings);
            }
            else
            {
                Bass.BASS_ChannelRemoveFX(monStream, _hpf);
                _hpf = 0;
            }
        }

        private int _lpf = 0;
        private void checkBoxPasaBajas_CheckedChanged(object sender, EventArgs e)
        {
            if (monStream == 0)
                return;

            if (this.checkBoxPasaBajas.Checked)
            {
                _lpf = Bass.BASS_ChannelSetFX(monStream, BASSFXType.BASS_FX_BFX_BQF, 0);
                BASS_BFX_BQF lpfSettings = new BASS_BFX_BQF(BASSBFXBQF.BASS_BFX_BQF_LOWPASS, (float)Convert.ToDouble(textBoxFrecBaja.Text),
                    0f, 0f, 0.7f, 0f, BASSFXChan.BASS_BFX_CHANALL);
                Bass.BASS_FXSetParameters(_lpf, lpfSettings);
            }
            else
            {
                Bass.BASS_ChannelRemoveFX(monStream, _lpf);
                _lpf = 0;
            }
        }


        private void buttonPasaAltas_Click(object sender, EventArgs e)
        {
            if (monStream == 0)
                return;

            BASS_BFX_BQF lpfSettings = new BASS_BFX_BQF(BASSBFXBQF.BASS_BFX_BQF_HIGHPASS, (float)Convert.ToDouble(textBoxFrecAlta.Text),
                0f, 0f, 0.7f, 0f, BASSFXChan.BASS_BFX_CHANALL);
            Bass.BASS_FXSetParameters(_lpf, lpfSettings);
        }

        private void buttonPasaBajas_Click(object sender, EventArgs e)
        {
            if (monStream == 0)
                return;

            BASS_BFX_BQF hpfSettings = new BASS_BFX_BQF(BASSBFXBQF.BASS_BFX_BQF_LOWPASS, (float)Convert.ToDouble(textBoxFrecBaja.Text),
                0f, 0f, 0.7f, 0f, BASSFXChan.BASS_BFX_CHANALL);
            Bass.BASS_FXSetParameters(_hpf, hpfSettings);
        }

        /*private int filtroPasaBandas = 0;
        private void checkBoxPasaBandas_CheckedChanged(object sender, EventArgs e)
        {
            if (monStream == 0)
                return;

            if (this.checkBoxPasaBandas.Checked)
            {
                filtroPasaBandas = Bass.BASS_ChannelSetFX(monStream, BASSFXType.BASS_FX_BFX_BQF, 0);
                BASS_BFX_BQF PasaBandasSettings = new BASS_BFX_BQF(BASSBFXBQF.BASS_BFX_BQF_BANDPASS, 1000f,
                    0f, 0f, 0.7f, 0f, BASSFXChan.BASS_BFX_CHANALL);
                Bass.BASS_FXSetParameters(filtroPasaBandas, PasaBandasSettings);
            }
            else
            {
                Bass.BASS_ChannelRemoveFX(monStream, filtroPasaBandas);
                filtroPasaBandas = 0;
            }
        }

        private int filtroPasaTodo = 0;
        private void checkBoxPasaTodo_CheckedChanged(object sender, EventArgs e)
        {
            if (monStream == 0)
                return;

            if (this.checkBoxPasaTodo.Checked)
            {
                filtroPasaTodo = Bass.BASS_ChannelSetFX(monStream, BASSFXType.BASS_FX_BFX_BQF, 0);
                BASS_BFX_BQF PasaTodoSettings = new BASS_BFX_BQF(BASSBFXBQF.BASS_BFX_BQF_ALLPASS, 1000f,
                    0f, 0f, 0.7f, 0f, BASSFXChan.BASS_BFX_CHANALL);
                Bass.BASS_FXSetParameters(filtroPasaTodo, PasaTodoSettings);
            }
            else
            {
                Bass.BASS_ChannelRemoveFX(monStream, filtroPasaTodo);
                filtroPasaTodo = 0;
            }
        }*/

        private void ActualizarEQ(int band, float gain)
        {
            BASS_DX8_PARAMEQ eq = new BASS_DX8_PARAMEQ();
            if (Bass.BASS_FXGetParameters(equalizador[band], eq))
            {
                eq.fGain = gain;
                Bass.BASS_FXSetParameters(equalizador[band], eq);
            }
        }

        private void trackBarEQBanda31_ValueChanged(object sender, EventArgs e)
        {
            ActualizarEQ(0, trackBarEQBanda31.Value / 10f);
        }

        private void trackBarEQBanda63_ValueChanged(object sender, EventArgs e)
        {
            ActualizarEQ(1, trackBarEQBanda63.Value / 10f);
        }

        private void trackBarEQBanda125_ValueChanged(object sender, EventArgs e)
        {
            ActualizarEQ(2, trackBarEQBanda125.Value / 10f);
        }

        private void trackBarEQBanda250_ValueChanged(object sender, EventArgs e)
        {
            ActualizarEQ(3, trackBarEQBanda250.Value / 10f);
        }

        private void trackBarEQBanda500_ValueChanged(object sender, EventArgs e)
        {
            ActualizarEQ(4, trackBarEQBanda500.Value / 10f);
        }

        private void trackBarEQBanda1000_ValueChanged(object sender, EventArgs e)
        {
            ActualizarEQ(5, trackBarEQBanda1000.Value / 10f);
        }

        private void trackBarEQBanda2000_ValueChanged(object sender, EventArgs e)
        {
            ActualizarEQ(6, trackBarEQBanda2000.Value / 10f);
        }

        private void trackBarEQBanda4000_ValueChanged(object sender, EventArgs e)
        {
            ActualizarEQ(7, trackBarEQBanda4000.Value / 10f);
        }

        private void trackBarEQBanda8000_ValueChanged(object sender, EventArgs e)
        {
            ActualizarEQ(8, trackBarEQBanda8000.Value / 10f);
        }

        private void trackBarEQBanda16000_ValueChanged(object sender, EventArgs e)
        {
            ActualizarEQ(9, trackBarEQBanda16000.Value / 10f);
        }

        private void pictureBoxEspectro_Click(object sender, EventArgs e)
        {
            espectroCompleto = !espectroCompleto;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            ArchivosAudio manejoArchivos = new ArchivosAudio();
            this.Hide();
            manejoArchivos.ShowDialog();
        }
    }
}
